﻿using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace testopenTk
{
    public class Sprite3D
    {
        /// <summary>
        /// 位置及旋转信息
        /// </summary>
        public SpirteInfo si=new SpirteInfo();
        /// <summary>
        /// 是否选中
        /// </summary>
        public bool touch = false;
        /// <summary>
        /// obj模型对象
        /// </summary>
        public LoadObj Obj_Model;
        static int ModelNameIndex = 1;
        /// <summary>
        /// obj类型 等待扩展...
        /// </summary>
        public string Model_Type { get;  }
        /// <summary>
        /// 模型（名字）索引
        /// </summary>
        public int nameIndex;
        /// <summary>
        /// 模型名字
        /// </summary>
        public string name;
        /// <summary>
        /// 模型路径
        /// </summary>
        internal string spath;
        public int layer_num = 0;
        float offset = 0;
        ~Sprite3D()
        {
        }
        /// <summary>
        /// 新建一个sprite3D 精灵
        /// </summary>
        /// <param name="FilePath">文件路径及文件名，包含后缀，只支持.obj</param>
        public Sprite3D(string FilePath)
        {
            spath = FilePath;
            si.Size = new System.Drawing.Size(242, 374);
            si.Visible = false;
            
            Program.Myform.Controls.Add(si);
            string[] sTemp = FilePath.Split('/','\\');
            nameIndex = ModelNameIndex;
            ModelNameIndex++;
            name = sTemp[sTemp.Length - 1];
            si.SetName(name);//设置名字,即文件名
            string[] t = FilePath.Split('.');
            if (t[t.Length-1]=="obj")
            {
                Model_Type = "obj";
                Obj_Model = new LoadObj(FilePath);
            }
           
        }
        public DrawInfo AssDraw;
    //    IntPtr myTest = IntPtr.Zero;
        float[] mytestFloat;
        
        
        public Sprite3D(string FilePath,DrawInfo di)
        {
            si.Size = new System.Drawing.Size(242, 374);
            si.Visible = false;
            spath = FilePath;
            try
            {
                Program.Myform.Invoke(new Action(() =>
                {
                    Program.Myform.Controls.Add(si);
                }));
            }
            catch { return; }
           
            
            string[] sTemp = FilePath.Split('/', '\\');
            nameIndex = ModelNameIndex;
            ModelNameIndex++;
            name = FilePath.Replace(System.Windows.Forms.Application.StartupPath + "\\", "");
            AssDraw = di;
            si.SetName(name+AssDraw.MeshIndex.ToString());
            //初始位置
            si.Location_X.Value += (decimal)AssDraw.Location.X;
            si.Location_Y.Value += (decimal)AssDraw.Location.Y;
            si.Location_Z.Value += (decimal)AssDraw.Location.Z;
            offset = AssDraw.Vao/10000f;
            touch = true;
            //GL.BindBuffer(BufferTarget.ArrayBuffer, AssDraw.verVbo);
            //myTest = GL.MapBuffer(BufferTarget.ArrayBuffer, BufferAccess.ReadWrite);
            //mytestFloat = new float[AssDraw.Count*3];
           
            //System.Runtime.InteropServices.Marshal.Copy(myTest,mytestFloat, 0, AssDraw.Count*3);
            //Vector3[] aaaa = new Vector3[AssDraw.Count];
            //System.Runtime.InteropServices.Marshal.PtrToStructure(myTest,aaaa);
            
            //for (int i = 0; i < AssDraw.Count; i++)
            //{
            //    //mytestFloat[i] = 0;
            //}
            //System.Runtime.InteropServices.Marshal.Copy(mytestFloat, 0, myTest, AssDraw.Count * 3);
            //GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(AssDraw.Count * sizeof(float) * 3),
            //    myTest, BufferUsageHint.StaticDraw);
        }
        Matrix4 modelview;
        bool theLastA = false;
        float rota = 0;
        float rota2 = 0;
        bool special = false;//teshu xianshi
        /// <summary>
        /// 绘制此模型，包含位置等信息
        /// </summary>
        /// <param name="select">是否为选中状态，若为true,则只绘制颜色，若为false，则绘制纹理</param>
        internal void Draw(bool select, double timeslice, Matrix4 mainCamera,decimal layer)
        {
            GL.Enable(EnableCap.PolygonOffsetFill);
            GL.PolygonOffset(offset, offset);
            //System.Runtime.InteropServices.Marshal.Copy()
            if (si.num_layer.Value != layer)
            {
                return;
            }
            special = false;
            //GL.DepthFunc(DepthFunction.)
            if (select)
            {
                GL.Uniform1(Shader.bOnlyColor, 1);
                GL.Uniform4(Shader.SelectColor, NameIndexToColor(nameIndex));
            }
            else
            {
                GL.Uniform1(Shader.bOnlyColor, 0);
            }

            modelview = Matrix4.CreateTranslation((float)si.Location_X.Value, (float)si.Location_Y.Value, (float)si.Location_Z.Value);
            modelview = Matrix4.CreateRotationX((float)si.Rotate_X.Value / 36) * modelview;
            modelview = Matrix4.CreateRotationY((float)si.Rotate_Y.Value / 36) * modelview;
            modelview = Matrix4.CreateRotationZ((float)si.Rotate_Z.Value / 36) * modelview;
            modelview *= mainCamera*Camera.Rota_Y;     //Camera的位置矩阵
            #region touch Animation
            if (touch)
            {
               
                switch ((int)si.num_Animation.Value)
                {
                    case 0:
                        //special = true;
                        //MyMessage.LabInfo("此物体选中时特殊显示");
                        //              Pointcalculate(ref mytestFloat, Program.Myform.timeslice / 1000);
                        //              GL.BindBuffer(BufferTarget.ArrayBuffer, AssDraw.verVbo);
                        //              if (!theLastA)
                        //              {

                        //                  GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(AssDraw.Count * sizeof(float) * 3),
                        //mytestFloat, BufferUsageHint.DynamicDraw);
                        //              }
                        //              else
                        //              {
                        //                  GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(AssDraw.Count * sizeof(float) * 3),
                        //AssDraw.vers, BufferUsageHint.StaticDraw);
                        //              }
                        //GL.PolygonMode(MaterialFace.Front, PolygonMode.Point);
                        //GL.PointSize(3);

                        break;
                    case 1:
                        MyMessage.LabInfo("此物体选中时将在此物体上方旋转");
                        modelview = Matrix4.CreateTranslation(0, 0.3f, 0) * Matrix4.CreateRotationY(rota += (float)Program.Myform.timeslice / 1000) * modelview;
                        break;
                    case 2:
                        MyMessage.LabInfo("此物体选中时将在摄像机前方旋转");
                        modelview = Matrix4.CreateRotationY(rota += (float)Program.Myform.timeslice / 1000)
                            * Matrix4.CreateTranslation(0, 0, -3);

                        break;
                    case 3:
                        MyMessage.LabInfo("每个物体都可以设置自己的动作，以方便观察");
                        modelview = Matrix4.CreateRotationZ(rota += (float)Program.Myform.timeslice / 1000)
                           * Matrix4.CreateRotationX(rota)
                           * Matrix4.CreateTranslation(0, 0, -3);
                        break;
                    case 7:
                        //touch = false;
                        Program.Myform.num_layer.Value++;
                        Camera.Location = Camera.MainCamera = Matrix4.Identity;
                        break;
                    case 8:
                        //touch = false;
                        Program.Myform.num_layer.Value--;
                        Camera.Location = Camera.MainCamera = Matrix4.Identity;
                        break;
                    case 9:
                        MyMessage.LabInfo("墙或地面不会有特殊显示或动作");
                        break;
                    default:
                        break;
                }


            }
            else
            {
                //GL.PolygonMode(MaterialFace.Front, PolygonMode.Fill);
            }
            #endregion
           
            switch ((int)si.num_Animation.Value)
            {
                //case 4:
                //    modelview = Matrix4.CreateRotationY(rota2 += (float)Program.Myform.timeslice / 1000)
                //       * modelview;
                //    break;
                //case 7:
                //case 8:
                //    modelview = Matrix4.CreateRotationY(rota2 += (float)Program.Myform.timeslice / 300)
                //        * modelview;
                //    anim(ref modelview, (float)Program.Myform.timeslice / 3000);
                //    break;

            }
            if (rota>float.MaxValue-1000||rota2>float.MaxValue-1000)
            {
                rota = 1;
                rota2 = 1;
            }
            GL.UniformMatrix4(Shader.ModelView, false, ref modelview);
            #region normal draw
            if (AssDraw != null)
            {
                if (AssDraw.Texture != 0)
                {
                    GL.Uniform1(Shader.bOnlyTexture, 1);
                }
                else
                {
                    GL.Uniform1(Shader.bOnlyTexture, 0);
                }

                if (AssDraw.material.HasColorAmbient)
                {
                    Assimp.Color4D c4 = AssDraw.material.ColorAmbient;
                    Color4 tk4 = new Color4(c4.R, c4.G, c4.B, c4.A);
                    GL.Uniform4(Shader.AmbientProduct, tk4);
                }
                if (AssDraw.material.HasColorDiffuse)
                {
                    Assimp.Color4D c4 = AssDraw.material.ColorDiffuse;
                    Color4 tk4 = new Color4(c4.R, c4.G, c4.B, c4.A);
                    GL.Uniform4(Shader.DiffuseProduct, tk4);
                }
                if (AssDraw.material.HasColorSpecular)
                {
                    Assimp.Color4D c4 = AssDraw.material.ColorSpecular;
                    Color4 tk4 = new Color4(c4.R, c4.G, c4.B, c4.A);
                    GL.Uniform4(Shader.SpecularProduct, tk4);
                }
                if (AssDraw.material.HasShininess)
                {
                    GL.Uniform1(Shader.Shininess, AssDraw.material.ShininessStrength);
                }
                if (AssDraw.material.HasColorEmissive)
                {
                    Assimp.Color4D c4 = AssDraw.material.ColorEmissive;
                    Color4 tk4 = new Color4(c4.R, c4.G, c4.B, c4.A);
                    GL.Uniform4(Shader.Emission, tk4);
                }

                GL.BindVertexArray(AssDraw.Vao);
                GL.BindTexture(TextureTarget.Texture2D, AssDraw.Texture);
                
                //Shadow.BindForReading(TextureUnit.Texture0);
                //if (AssDraw.material.HasShininess)
                //{
                //    GL.Uniform1(Shader.Shininess, AssDraw.material.Shininess);
                //}
                //if (AssDraw.material.HasColorEmissive)
                //{
                //    Assimp.Color4D c4 = AssDraw.material.ColorSpecular;
                //    float[] tk4 = { c4.R, c4.G, c4.B, c4.A };
                //    GL.Uniform4(Shader.Emission , 1, tk4);
                //}
                //GL.Enable(EnableCap.CullFace);
                GL.DrawElements(PrimitiveType.Triangles, AssDraw.Count, DrawElementsType.UnsignedInt, 0); 
            }
            #endregion
            if (touch)
            {
                si.Visible = true;
                if (!select && special)
                {
                    //选中特殊显示
                    GL.Uniform1(Shader.bOnlyColor, 1);
                    GL.Uniform4(Shader.SelectColor, new Color4(255, 0, 0, 50));
                    //modelview = Matrix4.CreateScale(1.1f, 1.1f, 1.1f) * modelview;
                    GL.UniformMatrix4(Shader.ModelView, false, ref modelview);

                    if (AssDraw != null)
                    {
                        GL.BindVertexArray(AssDraw.Vao);
                        GL.BindTexture(TextureTarget.Texture2D, AssDraw.Texture);
                        GL.DrawElements(PrimitiveType.Triangles, AssDraw.Count, DrawElementsType.UnsignedInt, 0);
                    }
                }
            }
            else
            {
               
                si.Visible = false;
            }
        }
        private int ColorToNameIndex(Color4 color)
        {
            return (int)((color.R + color.G * 256 + color.B * 256 * 256) * 255);
        }
        private Color4 NameIndexToColor(int index)
        {
            int r = 0xFF & index;
            int g = 0xFF00 & index;
            g >>= 8;
            int b = 0xFF0000 & index;
            b >>= 16;
            return new Color4(r/255f,g/255f, b/255f,1);
        }
        //public Sprite3D Touch
        public override string ToString()
        {
            string s = name + Environment.NewLine
                + si.Location_X.Value.ToString() + ","
                + si.Location_Y.Value.ToString() + ","
                + si.Location_Z.Value.ToString() + "," + Environment.NewLine
                + si.Rotate_X.Value.ToString() + ","
                + si.Rotate_Y.Value.ToString() + ","
                + si.Rotate_Z.Value.ToString() + Environment.NewLine;
            return s;
        }

        public void DisposeResoure()
        {
            if (si!=null)
            {
                si.Dispose();
            }
           
            if (Obj_Model != null)
            {
                Obj_Model.Dispose();
            }
        }
        float totalDis = 0;
        bool upOrdown = false;
        
        void anim(ref Matrix4 mv,float speed)
        {
            if (!upOrdown)
            {
                totalDis += speed;
                if (totalDis > 1)
                {
                    upOrdown = true;
                }
            }
            else
            {
                totalDis -= speed;
                if (totalDis <- 1)
                {
                    upOrdown = false;
                }

            }
           
           
            mv *= Matrix4.CreateTranslation(0, totalDis, 0);
        }

        public void collision(Matrix4 camera,Matrix4 lastCamera)
        {
            float x = camera.M41, y = camera.M42, z = camera.M43;
            float max_x = (float)si.Location_X.Value + AssDraw.max_box.X;
            float max_y = (float)si.Location_Y.Value + AssDraw.max_box.Y;
            float max_z = (float)si.Location_Z.Value + AssDraw.max_box.Z;
            float min_x = (float)si.Location_X.Value + AssDraw.min_box.X;
            float min_y = (float)si.Location_Y.Value + AssDraw.min_box.Y;
            float min_z = (float)si.Location_Z.Value + AssDraw.min_box.Z;
            //MyMessage.lable6Show("");
            if (x < max_x+0.3&& x > min_x- 0.3 &&
                y < max_y+1  && y > min_y  &&
                z < max_z + 0.3 && z > min_z- 0.3)
            {
                Camera.collision = true;
                //碰撞y轴的范围
                if (max_y + 1 - y < 1.8)
                {
                    y = max_y + 1;
                }
                else
                {
                    if (lastCamera.M41 < max_x + 0.3 && lastCamera.M41 > min_x - 0.3)
                    {
                        z = lastCamera.M43;
                    }
                    if (lastCamera.M43 < max_z + 0.3 && lastCamera.M43 > min_z - 0.3)
                    {
                        x = lastCamera.M41;
                    }
                }
                //MyMessage.lable6Show("in:" + name + AssDraw.MeshIndex.ToString());
                //touch = true;
            }
            Camera.SetCameraLocation(x, y, z);
        }

        float totalTime = 0;
        Random r = new Random();


        public void Pointcalculate(ref float[] list,double msa)
        {
            float ms= (float)msa;
            totalTime += ms;
           
            for (int i = 0; i < list.Length; i++)
            {
                int vec = r.Next(2) - 1;
                list[i++] += ms * vec;
                vec = r.Next(2) - 1;
                list[i++] += ms * vec;
                vec = r.Next(2) - 1;
                list[i] += ms * vec;
                
            }
            if (totalTime>2000)
            {
                totalTime = 0;
                theLastA = !theLastA;
                touch = false;
                //mytestFloat = AssDraw.vers.ToArray();
            }
        }
    }
}
